<?php
/*
 * 模板驱动类 
 * 创建于2014-07-14
 * 完善2017-09-13
 *
 * setCacheTime 设置缓存时间
 * assign 赋值变量
 * initFile 初始化模板文件地址
 * readTpl 读取模板
 * reBool 判断字符值是否存在，并返回指定类型的值
 * isCached 判断是否缓存
 * ziphtml 压缩html文件
 * 
 * {Tinwin include: 模板包含其他当前模板中文件
 * {Tinwin require: 模板包含其他非当前模板中文件
 * {Tinwin realhtml: 字符串中包含HTML标记
 * {Tinwin $ 调用assign赋值的变量
 * {Tinwin v: 在循环语句中使用
 * {Tinwin c: 调用系统中定义的常量
 *
 * {Tinwin date: 将字符转化为日期格式
 * {Tinwin substring: 字符串截取
 * {Tinwin realsubstring: 将截取的字符串包含HTML标记
 * {Tinwin eval: 
 * {Tinwin sql: 执行SQL语句
 * */
    class tpl{
        public $tpl_var = array();
        private $isCache = 1;
        private $cacheTime = 300; //缓存时间

        public $G;
        public $dir;
        public $cacheDir;

        public function __construct(&$G){
            $this->G = $G;

            $this->files = $this->G->loadclass('files');
            $this->routes = $this->G->loadclass('routes');
            $WebConfig=$this->G->loadclass('webconfig')->getWebConfig();
            if($this->G->app==HOMENAME){
		        $this->dir=$this->G->app.'/view/'.$WebConfig['website']['homeskin'].'/'.$this->G->module.'/';
            }else{
                $this->dir = $this->G->app.'/view/'.$this->G->module.'/';
            }
            define('SITE_ROOT',$WebConfig['website']['web_url']);
            define('STATIC_PATH',str_replace('\\','',SITE_ROOT.'static/'));
            define('PLUGIN_PATH',str_replace('\\','',SITE_ROOT.'plugin/'));
            define('UPLOAD_PATH_IMG', SITE_ROOT.'static/upload/image/'.date('Ymd').'/');
            define('ADMIN_URL',SITE_ROOT.'index.php?'.ADMINNAME.'-master-');
            define('HOME_URL',SITE_ROOT.'index.php?'.HOMENAME.'-web-');
            define('ADMIN_SKIN',SITE_ROOT.'application/'.ADMINNAME.'/view/master/');
            define('MySkin',SITE_ROOT.'application/'.$this->G->app.'/view/'.$this->G->module.'/');
            define('HomeSkinPath',SITE_ROOT.'application/'.HOMENAME.'/view/'.$WebConfig['website']['homeskin'].'/'.$this->G->module.'/');
            define('MyAppUrl', SITE_ROOT."index.php?".$this->G->app."-".$this->G->module."-");
            define('AppUrl', SITE_ROOT."index.php?".$this->G->app."-");
            define('UserCenterUrl',SITE_ROOT.'index.php?users-center-index');
			define('PoweredBy','Powered By <a href="http://www.chaojicms.com">ChaoJiCMS</a>');
            $this->assign('WebConfig',$WebConfig);
            $this->assign('CurSite',$WebConfig['website']);
            unset($WebConfig);
        }

        //设置缓存时间
        public function setCacheTime($time = false){
            if($time)$this->cacheTime = $time;
            else $this->isCache = 0;
        }

        //赋值变量
        public function assign($target,$vars){
            if(is_array($vars)){
                foreach($vars as $key => $cnt)
                    $this->tpl_var[$target][$key] = $vars[$key];
            }else{
                $this->tpl_var[$target] = $vars;
            }
        }

        //初始化模板文件地址
        public function initFile($app=NULL){
            if($app){
                $this->files->mdir(DATA_HTML.$app.'/view/'.$this->G->module.'/');
                $this->files->mdir(DATA_COMPILE.$app.'/view/'.$this->G->module.'/');
            }else{
                $this->files->mdir(DATA_HTML.$this->dir);
                $this->files->mdir(DATA_COMPILE.$this->dir);
            }
        }

        //读取模板
        public function readTpl($file){
            if(file_exists($file))return $this->files->readFile($file);
            else
                die('没有找到模板文件：'.$file);
        }

        //判断字符值是否存在，并返回指定类型的值
        public function reBool($str,$bool = 0){
            if($str)return intval($str);
            elseif($bool) return 1;
            else return 0;
        }

        //判断是否缓存
        public function isCached($file,$par = NULL,$cachename = NULL){
            $source = APP_PATH.$this->dir.$file.'.php';
            $outfile = DATA_COMPILE.$this->dir.$file.'.php';
            if($cachename)$outcache = DATA_HTML.$this->dir.$cachename.'.php';
            else $outcache = DATA_HTML.$this->dir.$file.$par.'.php';
            if(file_exists($outcache) && $this->isCache){
                if(((time()-filemtime($outcache))<= $this->cacheTime) && (filemtime($outfile) > filemtime($source))){
                    echo $this->files->readFile($outcache);
                    return true;
                }
            }
            return false;
        }

        public function isSimpleCached($cachename = NULL){
            if($cachename)$outcache = DATA_HTML.$this->dir.$cachename.'.php';
            else return false;
            if(file_exists($outcache) && $this->isCache){
                if((time()-filemtime($outcache))<= $this->cacheTime){
                    echo $this->files->readFile($outcache);
                    return true;
                }
            }
            return false;
        }

        //编译模板
        public function compileTpl($source){
            $content = $this->readTpl($source);
            $this->compileTree($content);
            $this->compileCat($content);
            $this->compileArt($content);
            $this->compileSpecialArt($content);
            $this->compileSpecialJdt($content);
            $this->compileArtList($content);
            $this->compileTagList($content);
            $this->compileEval($content);
            $this->compileSql($content);
            $this->compileIf($content);
            $this->compileInclude($content);
            $this->compileRequire($content);
            $this->compileArray($content);
            $this->compileDate($content);
            $this->compileRealSubstring($content);
            $this->compileSubstring($content);
            $this->compileRealVar($content);
            $this->compileEnter($content);
            $this->compileConst($content);
            return $content;
        }

        public function compileContentTpl($content){
            $this->compileTree($content);
            $this->compileCat($content);
            $this->compileArt($content);
            $this->compileSpecialArt($content);
            $this->compileSpecialJdt($content);
            $this->compileArtList($content);
            $this->compileTagList($content);
            $this->compileEval($content);
            $this->compileSql($content);
            $this->compileIf($content);
            $this->compileInclude($content);
            $this->compileRequire($content);
            $this->compileArray($content);
            $this->compileDate($content);
            $this->compileRealSubstring($content);
            $this->compileSubstring($content);
            $this->compileRealVar($content);
            $this->compileEnter($content);
            $this->compileConst($content);
            return $content;
        }

        public function compileInclude(&$content){
            $limit = '/{Tinwin include:(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \$this->_compileInclude('{$matches[1]}'); ?>";
            },$content);
        }

        public function _compileInclude($file){
            if($file)$this->fetch($file,NULL,0);
            if($this->isCache)include DATA_COMPILE.$this->dir.'/'.$file.'.php';
        }

        public function compileRequire(&$content){
            $limit = '/{Tinwin require:(\w+),(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \$this->_compileRequire('{$matches[1]}','{$matches[2]}'); ?>";
            },$content);
        }

        public function _compileRequire($file,$app=NULL){
            if($app){
                if($file)$this->fetch($file,$app,NULL,0);
                if($this->isCache)include DATA_COMPILE.$app.'/view/'.$this->G->module.'/'.$file.'.php';
            }else{
                if($file)$this->fetch($file,NULL,0);
                if($this->isCache)include DATA_COMPILE.$this->dir.'/'.$file.'.php';
            }  
        }

        public function compileRealVar(&$content){
            $limit = '/{Tinwin realhtml:([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo html_entity_decode(\$this->routes->stripSlashes(".$this->_compileArray($matches[1]).")); ?>";
            },$content);
        }

        public function compileVar(&$content){
            $limit = '/{Tinwin \$(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo \$this->tpl_var['{$matches[1]}']; ?>";
            },$content);
        }

        public function _compileVar($str){
            $limit = '/\$([\w|\']+)/';
            $str = preg_replace_callback($limit,function($matches){
                return "\$this->tpl_var['{$matches[1]}']";
            },$str);
            return $str;
        }

        public function compileTvar(&$content){
            $limit = '/{Tinwin v:([\w|\']+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo \${$matches[1]}; ?>";
            },$content);
        }

        public function _compileTvar($str){
            $limit = '/v:([\w|\']+)/';
            $str = preg_replace_callback($limit,function($matches){
                return "\${$matches[1]}";
            },$str);
            return $str;
        }

        public function compileConst(&$content){
            $limit = '/{Tinwin c:(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo {$matches[1]}; ?>";
            },$content);
        }

        public function compileArray(&$content){
            $limit = '/{Tinwin ([\$|v][\$|:|\[|\w|\]|\s|\']+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo ".$this->_compileArray($matches[1])."; ?>";
            },$content);
        }

        public function _compileArray($str){
            $str = $this->_compileVar($str);
            $str = $this->_compileTvar($str);
            return $str;
        }

        public function compileDate(&$content){
            $limit = '/{Tinwin date:([^,]+),([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo date({$matches[2]},".$this->_compileArray($matches[1])."); ?>";
            },$content);
        }

        public function compileSubstring(&$content){
            $limit = '/{Tinwin substring:([^,]+),([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo \$this->G->loadclass('strings')->subString(".$this->_compileArray($matches[1]).",$matches[2]); ?>";
            },$content);
        }

        public function compileRealSubstring(&$content){
            $limit = '/{Tinwin realsubstring:([^,]+),([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo \$this->G->loadclass('strings')->subString(strip_tags(html_entity_decode(\$this->routes->stripSlashes(".$this->_compileArray($matches[1])."))),$matches[2]); ?>";
            },$content);
        }

        public function compileEval(&$content){
            $limit = '/{Tinwin eval:([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php ".$this->_compileArray($this->routes->stripSlashes($matches[1]))."; ?>";
            },$content);
        }

        public function compileSql(&$content){
            $limit = '/{Tinwin sql:"([^"]+)"}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php var_dump($this->G->loadclass('dbsqli')->exec(".$this->_compileArray($matches[1]).")); ?>";
            },$content);
        }

        public function compileIf(&$content){
            $limit = '/{Tinwin if ([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php if(".$this->_compileArray($matches[1])."){ ?>";
            },$content);

            $limit = '/{Tinwin elseif ([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php } elseif(".$this->_compileArray($matches[1])."){ ?>";
            },$content);

            $limit = '/{Tinwin else}/';
            $replace = "<?php } else { ?>";
            $content = preg_replace($limit,$replace,$content);

            $limit = '/{Tinwin endif}/';
            $replace = "<?php } ?>";
            $content = preg_replace($limit,$replace,$content);
        }

        public function compileTree(&$content){
            $limit = '/{Tinwin tree ([^,]+),(\w+),(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \${$matches[3]} = 0;\n foreach(".$this->_compileArray($matches[1])." as \$key => \${$matches[2]}){ \n \${$matches[3]}++; ?>";
            },$content);

            $limit = '/{Tinwin endtree}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php } ?>";
            },$content);
        }

        /**
         * 根据分类ID获取分类 {TinCat 自定义变量，分类ID，排序} 创建：20171029 完善：20180620 最后更新时间：20190218
         * 排序 书写两边加单引号 多个字段用,隔开，降序在字段后面加上desc
         * 如：{TinCat mylist,0,'inrow desc,id'} 调用所有父ID为0的记录，并且按照inrow降序，inrow一样时再按照ID升序
         */
        public function compileCat(&$content){
            $limit = '/{TinCat (\w+),*(\d*),([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \$catlist=\$this->G->loadclass('category')->getSunByCid(".$this->reBool($matches[2]).",".$matches[3].");\n if(\$catlist){foreach(\$catlist as \$key => \${$matches[1]}){\n ?>";
            },$content);
            $limit = '/{WinCat}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php }} ?>";
            },$content);
        }

        //文章列表 {Tinwin artlist 标签，变量，分类ID，记录数} 创建：2017-10-29 完善：2018-06-20
        public function compileArt(&$content){
            $limit = '/{Tinwin artlist ([^,]+),(\w+),*(\d*),*(\d*)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \$i=0;\n foreach(".$this->_compileArray($matches[1])." as \$key => \${$matches[2]}){\n if(\${$matches[2]}['cat_id']==".$this->reBool($matches[3])."&&".$this->reBool($matches[4]).">\$i){\n \$i++; ?>";
            },$content);

            $limit = '/{Tinwin endart}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php }} ?>";
            },$content);
        }

        //根据文章分类ID及文章数获取文章 {TinArt 自定义变量，'分类ID'，记录数，'排序''} 创建：2017-10-29 完善：2018-06-20
        public function compileArtList(&$content){
            $limit = '/{TinArt (\w+),([^}]+),*(\d*),([^}]+),(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \$slist=\$this->G->loadclass('article')->getArtList(".$matches[2].",".$this->reBool($matches[3]).",".$matches[4].");\n if(\$slist){\n foreach(\$slist as \$key => \${$matches[1]}){\n ?>";
            },$content);
            $limit = '/{WinArt}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php }} ?>";
            },$content);
        }

        //根据文章分类ID及文章数获取文章 {TinSArt 自定义变量,分类ID,记录数,排序,循环次数变量} 创建：2019-01-02
        public function compileSpecialArt(&$content){
            $limit = '/{TinSArt (\w+),*(\d*),*(\d*),([^}]+),(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \${$matches[5]} = 0;\n \$slist=\$this->G->loadclass('article')->getSpecialArt(".$this->reBool($matches[2]).",".$this->reBool($matches[3]).",".$matches[4].");\n foreach(\$slist as \$key => \${$matches[1]}){ \n \${$matches[5]}++; ?>";
            },$content);
            $limit = '/{WinSArt}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php } ?>";
            },$content);
        }

        /**
         * 创建：2019-01-09
         * 作者：牛哥 tinwin@vip.qq.com
         * {TinJdt 自定义变量,记录数,排序,循环次数变量}
         * 
         */
        public function compileSpecialJdt(&$content){
            $limit = '/{TinJdt (\w+),*(\d*),([^}]+),(\w+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \${$matches[4]} = 0;\n \$slist=\$this->G->loadclass('jiaodiantu')->getSpecial(".$this->reBool($matches[2]).",".$matches[3].");\n foreach(\$slist as \$key => \${$matches[1]}){ \n \${$matches[4]}++; ?>";
            },$content);
            $limit = '/{WinJdt}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php } ?>";
            },$content);
        }

        //根据文章分类ID及文章数获取文章 {TinTag 自定义变量,记录数,'排序字段'} 创建：2018-09-23
        public function compileTagList(&$content){
            $limit = '/{TinTag (\w+),*(\d*),([^}]+)}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php \$slist=\$this->G->loadclass('tags')->getTagList(".$this->reBool($matches[2]).",".$matches[3].");\n foreach(\$slist as \$key => \${$matches[1]}){\n ?>";
            },$content);
            $limit = '/{WinTag}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php } ?>";
            },$content);
        }

        public function compileEnter(&$content){
            $limit = '/{Tinwin enter}/';
            $content = preg_replace_callback($limit,function($matches){
                return "<?php echo \"\n\"; ?>\n";
            },$content);
        }

        //解析模板
        public function fetch($file,$app='',$par='',$type = 0,$cachename = NULL){
            if($app){
                $this->initFile($app);
                $source = APP_PATH.$app.'/view/'.$this->G->module.'/'.$file.'.php';
                $outfile = DATA_COMPILE.$app.'/view/'.$this->G->module.'/'.$file.'.php';
                if($cachename)$outcache = DATA_HTML.$app.'/view/'.$this->G->module.'/'.$cachename.'.php';
                else $outcache = DATA_HTML.$app.'/view/'.$this->G->module.'/'.$file.$par.'.php';
            }else{
                $this->initFile();
                $source = APP_PATH.$this->dir.$file.'.php';
                $outfile = DATA_COMPILE.$this->dir.$file.'.php';
                if($cachename)$outcache = DATA_HTML.$this->dir.$cachename.'.php';
                else $outcache = DATA_HTML.$this->dir.$file.$par.'.php';
            }
            if((!file_exists($outfile)) || (filemtime($outfile) < filemtime($source))){
                $content = $this->compileTpl($source);
                $this->files->writeFile($outfile,$content);
                if($type){
                    include $outfile;
                    $this->files->delFile($outcache);
                }
            }else{
                if($this->isCache && (!file_exists($outcache) || (time() - filemtime($outcache)) > $this->cacheTime)){
                    if($type){
                        ob_start();
                        include $outfile;
                        $cachecontent = ob_get_contents();
                        $cachecontent=$this->ziphtml($cachecontent);
                        ob_flush();
                        $this->files->writeFile($outcache,$cachecontent);
                        ob_clean();
                    }
                }else{
                    include $outfile;
                }
            }
        }

        public function ziphtml($page_html){
            $page_html = str_replace("\r\n", '', $page_html); //清除换行符 
            $page_html = str_replace("\n", '', $page_html); //清除换行符 
            $page_html = str_replace("\t", '', $page_html); //清除制表符 
            $pattern = array ( 
                "/> *([^ ]*) *</", //去掉注释标记 
                "/[\s]+/", 
                "/<!--[^!]*-->/", 
                "/\" /", 
                "/ \"/", 
                "'/\*[^*]*\*/'" 
            ); 
            $replace = array ( 
                ">\\1<", 
                " ", 
                "", 
                "\"", 
                "\"", 
                "" 
            ); 
            $page_html = preg_replace($pattern, $replace, $page_html);
            return $page_html;
        }

        public function fetchContent($content){
            return $this->compileContentTpl($content);
        }

        public function fetchExeCnt($file){
            $source = APP_PATH.$this->dir.$file.'.php';
            $content = $this->compileTpl($source);
            ob_start();
            eval(' ?>'.$source.'<?php ');
            $cachecontent = ob_get_contents();
            ob_flush();
            ob_clean();
            return $cachecontent;
        }

        public function MakeHtml($file,$htmlfile,$tplpath){
            //$this->initFile();
            $source = APP_PATH.$tplpath.$file.'.php';
            $outfile = DATA_COMPILE.$tplpath.$file.'.php';
            //$outcache = DATA_HTML.$tplpath.$file.'.php';
            $content = $this->compileTpl($source);
            $this->files->writeFile($outfile,$content);
            ob_start();
            include $outfile;
            $cachecontent = ob_get_contents();
            $cachecontent=$this->ziphtml($cachecontent);
            ob_flush();
            $this->files->writeFile($htmlfile,$cachecontent);
            ob_clean();
        }

        //展示模板
        public function display($file,$app=NULL,$par=NULL,$cachename = NULL){
            $this->fetch($file,$app,$par,1,$cachename);
        }
    }
?>